package com.cardone.persistent.support;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.util.CollectionUtils;

import com.cardone.common.dto.PaginationDto;
import com.cardone.common.template.support.TemplateSupport;
import com.cardone.common.util.PaginationUtils;
import com.cardone.persistent.builder.Model;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * jdbc dao
 *
 * @author yaohaitao
 */
@Getter
@Setter
@Slf4j
@Accessors(chain = true)
public class NamedParameterJdbcTemplateSupport implements JdbcTemplateSupport {
	/**
	 * namedParameterJdbcTemplateRouterSupport
	 */
	private NamedParameterJdbcTemplateRouterSupport namedParameterJdbcTemplateRouterSupport;

	/**
	 * 模板支持
	 */
	private TemplateSupport templateSupport;

	@Override
	public int[] batchUpdate(final String id, final List<Map<String, Object>> batchValueList) {
		if (CollectionUtils.isEmpty(batchValueList)) {
			return new int[0];
		}

		final String sql = this.builderSql(id, null);

		final SqlParameterSource[] batchArgs = new SqlParameterSource[batchValueList.size()];

		int i = 0;

		for (final Map<String, ?> values : batchValueList) {
			batchArgs[i] = new MapSqlParameterSource(values);

			i++;
		}

		return this.getNamedParameterJdbcTemplate(id, null).batchUpdate(sql, batchArgs);
	}

	@Override
	public String builderSql(final String id, final Object model) {
		final String sql = this.templateSupport.process(id, model);

		return sql;
	}

	@Override
	public <R> R find(final Class<R> mappedClass, final String id, final Map<String, Object> model) {
		final List<R> rList = this.findList(mappedClass, id, model);

		if (CollectionUtils.isEmpty(rList)) {
			return null;
		}

		if (NamedParameterJdbcTemplateSupport.log.isErrorEnabled() && (rList.size() > 1)) {
			NamedParameterJdbcTemplateSupport.log.error("单条数据查询方法，查询出" + rList.size() + "条数据:id=" + id + ",model=" + model);
		}

		return rList.get(0);
	}

	@Override
	public Map<String, Object> find(final String id, final Map<String, Object> model) {
		final List<Map<String, Object>> mapList = this.findList(id, model);

		if (CollectionUtils.isEmpty(mapList)) {
			return null;
		}

		if (NamedParameterJdbcTemplateSupport.log.isErrorEnabled() && (mapList.size() > 1)) {
			NamedParameterJdbcTemplateSupport.log.error("单条数据查询方法，查询出" + mapList.size() + "条数据:id=" + id + ",model=" + model);
		}

		return mapList.get(0);
	}

	@Override
	public <P> List<P> findList(final Class<P> mappedClass, final String id) {
		return this.findList(mappedClass, id, null);
	}

	@Override
	public <R> List<R> findList(final Class<R> mappedClass, final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		return this.getNamedParameterJdbcTemplate(id, model).query(sql, model, ParameterizedBeanPropertyRowMapper.newInstance(mappedClass));
	}

	@Override
	public List<Map<String, Object>> findList(final String id) {
		return this.findList(id, null);
	}

	@Override
	public List<Map<String, Object>> findList(final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		return this.getNamedParameterJdbcTemplate(id, model).queryForList(sql, model);
	}

	@Override
	public org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(final String value, final Object model) {
		return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(value, model);
	}

	@Override
	public <R> PaginationDto<R> pagination(final Class<R> mappedClass, final PaginationArgs paginationArgs, final Map<String, Object> model) {
		final Map<String, Object> newModel = Maps.newHashMap(model);

		final long totalSize = this.read(Long.class, paginationArgs.getReadId(), newModel);

		final PaginationDto<R> pagination = PaginationUtils.newPagination(paginationArgs.getNo(), paginationArgs.getSize(), 6, totalSize);

		if ((totalSize < 1) || (totalSize < pagination.getBeginRowNum())) {
			return pagination.setDataList(Lists.<R> newArrayList());
		}

		final String beginKey = newModel.containsKey(Model.Keys.beginRowNum.stringValue()) ? Model.Keys.paginationBeginRowNum.stringValue() : Model.Keys.beginRowNum.stringValue();

		newModel.put(beginKey, pagination.getBeginRowNum());

		final String endKey = newModel.containsKey(Model.Keys.endRowNum.stringValue()) ? Model.Keys.paginationEndRowNum.stringValue() : Model.Keys.endRowNum.stringValue();

		newModel.put(endKey, pagination.getEndRowNum());

		final String sizeKey = newModel.containsKey(Model.Keys.size.stringValue()) ? Model.Keys.paginationSize.stringValue() : Model.Keys.size.stringValue();

		newModel.put(sizeKey, pagination.getSize());

		final List<R> dataList = this.findList(mappedClass, paginationArgs.getFindId(), newModel);

		pagination.setDataList(dataList);

		return pagination;
	}

	@Override
	public PaginationDto<Map<String, Object>> pagination(final PaginationArgs paginationArgs, final Map<String, Object> model) {
		final Map<String, Object> newModel = Maps.newHashMap(model);

		final long totalSize = this.read(Long.class, paginationArgs.getReadId(), null, newModel);

		final PaginationDto<Map<String, Object>> pagination = PaginationUtils.newPagination(paginationArgs.getNo(), paginationArgs.getSize(), 6, totalSize);

		if ((totalSize < 1) || (totalSize < pagination.getBeginRowNum())) {
			return pagination.setDataList(Lists.<Map<String, Object>> newArrayList());
		}

		final String beginKey = newModel.containsKey(Model.Keys.beginRowNum.stringValue()) ? Model.Keys.paginationBeginRowNum.stringValue() : Model.Keys.beginRowNum.stringValue();

		newModel.put(beginKey, pagination.getBeginRowNum());

		final String endKey = newModel.containsKey(Model.Keys.endRowNum.stringValue()) ? Model.Keys.paginationEndRowNum.stringValue() : Model.Keys.endRowNum.stringValue();

		newModel.put(endKey, pagination.getEndRowNum());

		final String sizeKey = newModel.containsKey(Model.Keys.size.stringValue()) ? Model.Keys.paginationSize.stringValue() : Model.Keys.size.stringValue();

		newModel.put(sizeKey, pagination.getSize());

		final List<Map<String, Object>> dataList = this.findList(paginationArgs.getFindId(), newModel);

		pagination.setDataList(dataList);

		return pagination;
	}

	@Override
	public <R> R read(final Class<R> requiredType, final String id) {
		return this.read(requiredType, id, new HashMap<String, Object>());
	}

	@Override
	public <R> R read(final Class<R> requiredType, final String id, final Map<String, Object> model) {
		final List<R> rList = this.readList(requiredType, id, model);

		if (org.apache.commons.collections.CollectionUtils.isEmpty(rList)) {
			return null;
		}

		if (NamedParameterJdbcTemplateSupport.log.isErrorEnabled() && (rList.size() > 1)) {
			NamedParameterJdbcTemplateSupport.log.error("单个值查询方法，查询出" + rList.size() + "个值:id=" + id + ",model=" + model);
		}

		return rList.get(0);
	}

	@Override
	public <R> R read(final Class<R> requiredType, final String id, final String objectId) {
		return this.read(requiredType, id, objectId, new HashMap<String, Object>());
	}

	@Override
	public <R> R read(final Class<R> requiredType, final String id, final String objectId, final Map<String, Object> model) {
		final Map<String, Object> newModel = Maps.newHashMap(model);

		if (StringUtils.isNotBlank(objectId)) {
			newModel.put(Model.Keys.objectId.stringValue(), objectId);
		}

		return this.read(requiredType, id, newModel);
	}

	@Override
	public <R> List<R> readList(final Class<R> elementType, final String id) {
		return this.readList(elementType, id, Model.Keys.objectId.stringValue(), new HashMap<String, Object>());
	}

	@Override
	public <R> List<R> readList(final Class<R> elementType, final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		return this.getNamedParameterJdbcTemplate(id, model).queryForList(sql, model, elementType);
	}

	@Override
	public <R> List<R> readList(final Class<R> elementType, final String id, final String objectId) {
		return this.readList(elementType, id, objectId, new HashMap<String, Object>());
	}

	@Override
	public <R> List<R> readList(final Class<R> elementType, final String id, final String objectId, final Map<String, Object> model) {
		final Map<String, Object> newModel = Maps.newHashMap(model);

		if (StringUtils.isNotBlank(objectId)) {
			newModel.put(Model.Keys.objectId.stringValue(), objectId);
		}

		return this.readList(elementType, id, newModel);
	}

	@Override
	public int update(final List<String> idList, final Map<String, Object> model) {
		int updateCount = 0;

		for (final String id : idList) {
			updateCount += this.update(id, model);
		}

		return updateCount;
	}

	@Override
	public int update(final String id) {
		final String sql = this.builderSql(id, null);

		return this.getNamedParameterJdbcTemplate(id, null).getJdbcOperations().update(sql);
	}

	@Override
	public int update(final String id, final List<Map<String, Object>> modelList) {
		if (CollectionUtils.isEmpty(modelList)) {
			return 0;
		}

		int rowCount = 0;

		for (final Map<String, Object> model : modelList) {
			rowCount += this.update(id, model);
		}

		return rowCount;
	}

	@Override
	public int update(final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		return this.getNamedParameterJdbcTemplate(id, model).update(sql, model);
	}
}
